package sunwin.yoms.service.resource;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import jodd.datetime.JDateTime;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import sunwin.yoms.comm.LogConst;
import sunwin.yoms.comm.YomsException;
import sunwin.yoms.comm.YomsHelper;
import sunwin.yoms.comm.YomsPage;
import sunwin.yoms.comm.model.DateTimeSpan;
import sunwin.yoms.comm.model.NameAndValueModel;
import sunwin.yoms.comm.model.TimeSpan;
import sunwin.yoms.dao.SubscribeOrderDao;
import sunwin.yoms.domain.resource.StadiumDomain;
import sunwin.yoms.domain.resource.SubscribeExceptionDomain;
import sunwin.yoms.domain.resource.SubscribeOrderDomain;
import sunwin.yoms.domain.resource.SubscribeReportFormsDomain;
import sunwin.yoms.service.comm.exception.SubscribeErrorException;
import sunwin.yoms.service.comm.exception.SubscribeOccupiedException;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * Created by mt on 2015-9-18.
 */
@Service
public class SubscribeOrderService {
    private int maxDay = 100;
    private int maxWeek = 14;
    private int maxMonth = 3;

    @Autowired
    private StadiumService stadiumService;
    @Autowired
    private SubscribeOrderDao subscribeOrderDao;
    @Autowired
    private SubscribeExceptionService subscribeExceptionService;
    @Autowired
    private YomsHelper yomsHelper;
    @Autowired
    JmsTemplate jmsTemplate;

    /**
     * 根据用户ID获取订单
     * 用于查询预约订单
     *
     * @param uid
     */
    public List<StadiumDomain> getSubscribeStadiumsByUid(int uid, YomsPage yomsPage) throws YomsException {
        List<StadiumDomain> stadiumDomains = new ArrayList<>();

        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderUserId(uid);

        DateTimeSpan dateTimeSpan = new DateTimeSpan();
        dateTimeSpan.setStartDateTime(new JDateTime(new JDateTime().toString("YYYY-MM-DD")).convertToDate());
        dateTimeSpan.setEndDateTime(new JDateTime().addYear(1000).convertToDate());

        Page page = PageHelper.startPage(yomsPage.getPageIndex(), yomsPage.getPageSize(), true);
        List<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getSubscribeStadiumsByUid(orderDomain, dateTimeSpan);
        yomsPage.setPageCount(page.getPages());

        for (SubscribeOrderDomain subscribeOrderDomain : subscribeOrderDomains) {
            StadiumDomain stadiumDomain = stadiumService.getStadiumById(subscribeOrderDomain.getSubscribeOrderStadiumId());
            stadiumDomains.add(stadiumDomain);
        }

        return stadiumDomains;
    }

    /**
     * 根据用户ID和场馆ID获取预约的日期
     * 用于查询预约订单
     *
     * @param uid
     * @param sid
     * @return
     * @throws YomsException
     */
    public List<JDateTime> getSubscribeDateByUidWithSid(int uid, int sid) throws YomsException {
        List<JDateTime> dates = new ArrayList<>();

        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderUserId(uid);
        orderDomain.setSubscribeOrderStadiumId(sid);

        DateTimeSpan dateTimeSpan = new DateTimeSpan();
        dateTimeSpan.setStartDateTime(new JDateTime(new JDateTime().toString("YYYY-MM-DD")).convertToDate());
        dateTimeSpan.setEndDateTime(new JDateTime(new JDateTime().toString("YYYY-MM-DD")).addYear(1000).convertToDate());

        List<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getSubscribeDateByUidWithSid(orderDomain, dateTimeSpan);

        for (SubscribeOrderDomain subscribeOrderDomain : subscribeOrderDomains) {
            dates.add(new JDateTime(subscribeOrderDomain.getSubscribeOrderDate()));
        }

        return dates;
    }

    /**
     * 根据用户ID，场馆ID，预约日期获取时间段
     *
     * @param uid
     * @param sid
     * @param dateTime
     * @return
     * @throws YomsException
     */
    public List<TimeSpan> getSubscribeDateTimeSpanByUidWithSidWithDate(int uid, int sid, JDateTime dateTime) throws YomsException {
        List<TimeSpan> dateTimeSpans = new ArrayList<>();

        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderUserId(uid);
        orderDomain.setSubscribeOrderStadiumId(sid);
        orderDomain.setSubscribeOrderDate(dateTime.convertToDate());

        List<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getDomainByConditionOfSingleTable(orderDomain);

        if (subscribeOrderDomains.size() == 1) {
            long timeSpan = subscribeOrderDomains.get(0).getSubscribeOrderTimeSpan();

            dateTimeSpans = convertLongToTimespans(sid, timeSpan);
        }

        return dateTimeSpans;
    }

    /**
     * 根据场馆ID获取场馆时间段---这里获取的是该场馆所有的时间段
     */
    public List<TimeSpan> getSubscribeDateTimeSpanBySid(int sid) throws YomsException {
        TimeSpan timeSpan = stadiumService.getStadiumBusinessTimeSpan(sid);
        List<TimeSpan> timeSpans = businessHours(timeSpan);
        return timeSpans;
    }

    /**
     * 取消某一天的预约订单
     *
     * @param uid
     * @param sid
     * @param dateTime
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void cancelSubscribe(int uid, int sid, JDateTime dateTime) throws Exception {
        SubscribeOrderDomain domain = new SubscribeOrderDomain();
        domain.setSubscribeOrderUserId(uid);
        domain.setSubscribeOrderStadiumId(sid);
        domain.setSubscribeOrderDate(dateTime.convertToDate());

        List<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getDomainByConditionOfSingleTable(domain);

        for (SubscribeOrderDomain subscribeOrderDomain1 : subscribeOrderDomains) {
            subscribeOrderDao.deleteDomain(subscribeOrderDomain1);
        }
        SimpleDateFormat stf = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = dateTime.convertToDate();
        String dateString = stf.format(date1);
        jmsTemplate.send("CancelOrder", new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                String msg = "你好：" + "你的预约日期为：" + dateString + "的预约订单已取消,详情请上官方网站查询";
                Logger.getLogger(LogConst.db).info(msg);
                return session.createObjectMessage(msg);
            }
        });
    }

    /**
     * 给增加例外计划调用的接口
     *
     * @param sid
     * @param date
     * @param timeSpanLong
     * @throws Exception
     */
    public synchronized void cancelSubscribe(int sid, Date date, long timeSpanLong) throws Exception {
        cancelSubscribe(0, sid, date, timeSpanLong);
    }

    /**
     * 取消预约订单中某一天某些时间段
     */
    public synchronized void cancelSubscribe(int uid, int sid, Date date, long timeSpanLong) throws Exception {
        //获取某一天所有场馆的预约订单
        SubscribeOrderDomain domain = new SubscribeOrderDomain();
        domain.setSubscribeOrderStadiumId(sid);
        domain.setSubscribeOrderDate(date);
        if (uid > 0) {
            domain.setSubscribeOrderUserId(uid);
        }
        List<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getDomainByConditionOfSingleTable(domain);

        subscribeOrderDomains.forEach(s -> {
            try {
                long tempSpansLong = s.getSubscribeOrderTimeSpan();
                long iteration = 2;
                for (int i = 0; i < 63; i++) {
                    if ((timeSpanLong & iteration) == iteration) {
                        if ((tempSpansLong & iteration) == iteration) {
                            tempSpansLong = tempSpansLong - iteration;
                        }
                    }
                    iteration = iteration << 1;
                }
                s.setSubscribeOrderTimeSpan(tempSpansLong);
                if (tempSpansLong == 0) {
                    subscribeOrderDao.deleteDomain(s);
                    SubscribeOrderDomain subDomain = subscribeOrderDao.getDomainById(s.getSubscribeOrderId());
                    SimpleDateFormat stf = new SimpleDateFormat("yyyy-MM-dd");
                    Date date1 = s.getSubscribeOrderDate();
                    String dateString = stf.format(date1);
                    //取消预约时将消息写入队列
                    jmsTemplate.send("CancelOrder", new MessageCreator() {
                        @Override
                        public Message createMessage(Session session) throws JMSException {
                            String msg = subDomain.getUserDomain().getUserLoginName() + "你好" + ",你的预约订单为" + s.getSubscribeOrderNo() + ",日期为：" + dateString + "已取消,详情请上官方网站查询";
                            Logger.getLogger(LogConst.db).info(msg);
                            return session.createObjectMessage(msg);
                        }
                    });
                } else {
                    subscribeOrderDao.updateDomain(s);
                    //取消预约时将消息写入队列
                    SubscribeOrderDomain subDomain = subscribeOrderDao.getDomainById(s.getSubscribeOrderId());
                    SimpleDateFormat stf = new SimpleDateFormat("yyyy-MM-dd");
                    Date date2 = s.getSubscribeOrderDate();
                    String dateString = stf.format(date2);
                    jmsTemplate.send("CancelOrder", new MessageCreator() {
                        @Override
                        public Message createMessage(Session session) throws JMSException {
                            String msg = subDomain.getUserDomain().getUserLoginName() + ":你好" + ",你的预约订单为" + s.getSubscribeOrderNo() + ",日期为：" + dateString + "已取消,详情请上官方网站查询";
                            Logger.getLogger(LogConst.db).info(msg);
                            return session.createObjectMessage(msg);
                        }
                    });
                }
            } catch (NullPointerException e) {
                Logger.getLogger(LogConst.SUNWIN).error("JMS空指针异常：" + e);
            }
        });

    }


    /**
     * 批量取消预约
     *
     * @param uid
     * @param sid
     * @param startDate
     * @param endDate
     * @throws Exception
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void batchCancelSubscribe(int uid, int sid, JDateTime startDate, JDateTime endDate) throws Exception {
        JDateTime currentDate = new JDateTime(new JDateTime().toString("YYYY-MM-DD"));

        if (startDate.isBefore(currentDate)) {
            throw new SubscribeErrorException("批量取消预约起始日期必须大于当前日期");
        }

        if (startDate.isBefore(endDate)) {
            while (startDate.isBefore(endDate)) {
                cancelSubscribe(uid, sid, startDate);
                startDate.addDay(1);
            }
            cancelSubscribe(uid, sid, endDate);
        } else {
            throw new SubscribeErrorException("批量取消预约起始日期必须小于截至日期");
        }
    }

    /**
     * 周期天预约
     * 用于预约场馆
     *
     * @param sid
     * @param uid
     * @param startDate
     * @param endDate
     * @param timeSpans
     * @param isRandom
     * @throws Exception
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void everyDayOfSubscribe(int sid, int uid, JDateTime startDate, JDateTime endDate, List<TimeSpan> timeSpans, boolean isRandom) throws Exception {
        JDateTime tempStartDate = new JDateTime(startDate.toString("YYYY-MM-DD"));
        String orderNo = buildOrderNo();

        if (tempStartDate.addDay(maxDay).isBefore(endDate)) {
            throw new SubscribeErrorException("预约截至时间最大不能超过开始时间" + maxDay + "天");
        }

        if (startDate.isAfter(endDate)) {
            throw new SubscribeErrorException("预约时间非法");
        }
        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        //设置主订单owner
        if (startDate.isBefore(endDate)) {
            try {
                orderDomain = oneDayOfSubscribe1(sid, uid, orderNo, startDate, timeSpans, isRandom);
            } catch (SubscribeOccupiedException ex) {
                if (!isRandom) {
                    throw ex;
                }
            }
        }
        if (orderDomain.getSubscribeOrderId() > 0) {
            //设置子订单owner
            JDateTime otherDate = startDate.addDay(1);
            while (otherDate.isBefore(endDate)) {
                try {
                    oneDayOfSubscribe2(sid, uid, orderNo, otherDate, timeSpans, isRandom, orderDomain);
                } catch (SubscribeOccupiedException ex) {
                    if (!isRandom) {
                        throw ex;
                    }
                }
                otherDate.addDay(1);
            }
        } else {
            throw new YomsException("预约失败");
        }

    }

    /**
     * 周期星期预约
     * 用于预约场馆
     *
     * @param sid
     * @param uid
     * @param startDate
     * @param endDate
     * @param timeSpans
     * @param isRandom
     * @throws Exception
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void everyWeekOfSubscribe(int sid, int uid, JDateTime startDate, JDateTime endDate, List<TimeSpan> timeSpans, boolean isRandom) throws Exception {
        JDateTime tempStartDate = new JDateTime(startDate.toString("YYYY-MM-DD"));
        String orderNo = buildOrderNo();

        if (tempStartDate.addDay(maxWeek * 7).isBefore(endDate)) {
            throw new SubscribeErrorException("预约截至时间最大不能超过开始时间" + maxWeek + "个星期");
        }

        if (startDate.isAfter(endDate)) {
            throw new SubscribeErrorException("预约时间非法");
        }
        //主订单owner
        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();

        if (startDate.isBefore(endDate)) {
            try {
                orderDomain = oneDayOfSubscribe1(sid, uid, orderNo, startDate, timeSpans, isRandom);
            } catch (SubscribeOccupiedException ex) {
                if (!isRandom) {
                    throw ex;
                }
            }
        }
        if (orderDomain.getSubscribeOrderId() > 0) {
            //设置子订单owner
            JDateTime nextDate = startDate.addDay(7);
            while (nextDate.isBefore(endDate)) {
                try {
                    oneDayOfSubscribe2(sid, uid, orderNo, nextDate, timeSpans, isRandom, orderDomain);
                } catch (SubscribeOccupiedException ex) {
                    if (!isRandom) {
                        throw ex;
                    }
                }
                nextDate.addDay(7);
            }
        } else {
            throw new YomsException("预约失败");
        }
    }

    /**
     * 周期月预约
     * 用于预约场馆
     *
     * @param sid
     * @param uid
     * @param startDate
     * @param endDate
     * @param timeSpans
     * @param isRandom
     * @throws Exception
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void everyMonthOfSubscribe(int sid, int uid, JDateTime startDate, JDateTime endDate, List<TimeSpan> timeSpans, boolean isRandom) throws Exception {
        JDateTime tempStartDate = new JDateTime(startDate.toString("YYYY-MM-DD"));
        String orderNo = buildOrderNo();

        if (tempStartDate.addMonth(maxMonth).isBefore(endDate)) {
            throw new SubscribeErrorException("预约截至时间最大不能超过开始时间" + maxMonth + "个月");
        }

        if (startDate.isAfter(endDate)) {
            throw new SubscribeErrorException("预约时间非法");
        }

        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();

        if (startDate.isBefore(endDate)) {
            try {
                orderDomain = oneDayOfSubscribe1(sid, uid, orderNo, startDate, timeSpans, isRandom);
            } catch (SubscribeOccupiedException ex) {
                if (!isRandom) {
                    throw ex;
                }
            }
        }
        if (orderDomain.getSubscribeOrderId() > 0) {
            //设置子订单owner
            JDateTime nextDate = startDate.addMonth(1);
            while (nextDate.isBefore(endDate)) {
                try {
                    oneDayOfSubscribe2(sid, uid, orderNo, nextDate, timeSpans, isRandom, orderDomain);
                } catch (SubscribeOccupiedException ex) {
                    if (!isRandom) {
                        throw ex;
                    }
                }
                nextDate.addMonth(1);
            }
        } else {
            throw new YomsException("预约失败");
        }
    }

    /**
     * 第一天的预约
     * 用于预约场馆
     *
     * @param sid
     * @param uid
     * @param date
     * @param timeSpans
     * @param isRandom
     * @throws Exception
     */

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized SubscribeOrderDomain oneDayOfSubscribe1(int sid, int uid, String orderNo, JDateTime date, List<TimeSpan> timeSpans, boolean isRandom) throws Exception {
        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderStadiumId(sid);
        orderDomain.setSubscribeOrderUserId(uid);
        orderDomain.setSubscribeOrderStateId(1);
        orderDomain.setSubscribeOrderVerifyStateId(1);
        orderDomain.setSubscribeOrderOwner(0);
        orderDomain.setSubscribeOrderDate(date.convertToDate());
        orderDomain.setSubscribeOrderCreateDate(new JDateTime().convertToDate());
        orderDomain.setSubscribeOrderTimeSpan(convertTimeSpansToLong(timeSpans));
        if (orderNo != null) {
            orderDomain.setSubscribeOrderNo(orderNo);
        } else {
            orderDomain.setSubscribeOrderNo(buildOrderNo());
        }
        SubscribeOrderDomain domain = subscribe(orderDomain, isRandom);
        //获取主订单
        return domain;
    }


    /**
     * 周期后N天的预约
     * 用于预约场馆
     *
     * @param sid
     * @param uid
     * @param date
     * @param timeSpans
     * @param isRandom
     * @throws Exception
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public synchronized void oneDayOfSubscribe2(int sid, int uid, String orderNo, JDateTime date, List<TimeSpan> timeSpans, boolean isRandom, SubscribeOrderDomain domain) throws Exception {
        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderStadiumId(sid);
        orderDomain.setSubscribeOrderUserId(uid);
        orderDomain.setSubscribeOrderStateId(1);
        orderDomain.setSubscribeOrderVerifyStateId(1);
        orderDomain.setSubscribeOrderOwner(domain.getSubscribeOrderId());
        orderDomain.setSubscribeOrderDate(date.convertToDate());
        orderDomain.setSubscribeOrderCreateDate(new JDateTime().convertToDate());
        orderDomain.setSubscribeOrderTimeSpan(convertTimeSpansToLong(timeSpans));
        if (orderNo != null) {
            orderDomain.setSubscribeOrderNo(orderNo);
        } else {
            orderDomain.setSubscribeOrderNo(buildOrderNo());
        }
        subscribe(orderDomain, isRandom);
    }

    /**
     * 生成订单编号
     * 用于预约场馆
     *
     * @return
     */
    public String buildOrderNo() {
        return new JDateTime().toString("YYYYMMDDhhmmssmss");
    }

    /**
     * 开始某一天预约流程
     * 在预期预约的时间段排除已经预约的时间段
     * 根据已经预约的时间段找出预期预约时间段中不可预约的时间段
     * 最终得出可预约的时间段,如果某个用户某一天预约了某个场馆则在原先预约的记录加上可预约的时间段,否则创建一条新的预约记录
     *
     * @param orderDomain
     * @param isRandom
     * @throws Exception
     */
    private SubscribeOrderDomain subscribe(SubscribeOrderDomain orderDomain, boolean isRandom) throws Exception {
        int sid = orderDomain.getSubscribeOrderStadiumId();

        //检查场馆是否正常使用
        StadiumDomain stadiumDomain = stadiumService.getStadiumById(sid);
        if (stadiumDomain.getStadiumStateDomain().getStadiumStateId() != 1) {
            throw new SubscribeErrorException(stadiumDomain.getStadiumName() + "场馆处于" + stadiumDomain.getStadiumStateDomain().getStadiumStateName() + "状态");
        }

        //获取场馆营业时间
        TimeSpan businessTimeSpan = stadiumService.getStadiumBusinessTimeSpan(sid);

        //获取某个场馆某一天已经预约的时间段
        Collection<SubscribeOrderDomain> subscribeOrderDomains = getOneDayOrder(orderDomain.getSubscribeOrderStadiumId(), new JDateTime(orderDomain.getSubscribeOrderDate()));
        long existLong = subscribeOrderDomains
                .stream()
                .map(s -> s.getSubscribeOrderTimeSpan())
                .collect(toList())
                .stream()
                .reduce(0L, (acc, element) -> acc + element);
        Collection<TimeSpan> existTimeSpans = this.convertLongToTimespans(businessTimeSpan, existLong);
        Collection<String> existTimeSpansString = existTimeSpans
                .stream()
                .map(s -> s.toString())
                .collect(toList());

        //获取例外计划时间段
        SubscribeExceptionDomain subscribeExceptionDomain = new SubscribeExceptionDomain();
        subscribeExceptionDomain.setStadiumId(sid);
        subscribeExceptionDomain.setExceptionDate(orderDomain.getSubscribeOrderDate());

        SubscribeExceptionDomain factSubscribeExceptionDomain = subscribeExceptionService.getSubExceptionDamain(subscribeExceptionDomain);
        Long factSubscribeTimeSpanLong = factSubscribeExceptionDomain.getExceptionTimeBucket();
        Collection<TimeSpan> factSubscribeTimeSpans = this.convertLongToTimespans(sid, factSubscribeTimeSpanLong);
        Collection<String> factSubscribeTimeSpanStrings = factSubscribeTimeSpans
                .stream()
                .map(s -> s.toString())
                .collect(toList());

        //转换预期预约的时间段
        Collection<TimeSpan> expectTimeSpans = this.convertLongToTimespans(businessTimeSpan, orderDomain.getSubscribeOrderTimeSpan());
        Collection<String> expectTimeSpansString = expectTimeSpans
                .stream()
                .map(s -> s.toString())
                .collect(toList());

        //不可预约的时间段
        Collection<TimeSpan> tempInvalidTimeSpans1 = expectTimeSpans
                .stream()
                .filter(s -> existTimeSpansString.contains(s.toString()))
                .collect(toList());

        Collection<TimeSpan> tempInvalidTimeSpans2 = expectTimeSpans
                .stream()
                .filter(s -> factSubscribeTimeSpanStrings.contains(s.toString()))
                .collect(toList());

        Collection<TimeSpan> invalidTimeSpans = Stream
                .of(tempInvalidTimeSpans1, tempInvalidTimeSpans2)
                .flatMap(s -> s.stream())
                .collect(toList());

        //可预约的时间段
        Collection<TimeSpan> availabilityTimeSpans = expectTimeSpans
                .stream()
                .filter(s -> !existTimeSpansString.contains(s.toString()))
                .collect(toList());

        //是否忽略已存在的时间段和例外时间段
        if (!isRandom) {
            if (invalidTimeSpans.size() != 0) {
                SubscribeOccupiedException yomsException = new SubscribeOccupiedException("预约失败，您选择的部分时间段已经被预约");
                yomsException.setOccupiedTimeSpans(convertTimeSpansToNameWithValues((List<TimeSpan>) invalidTimeSpans,
                        new JDateTime(orderDomain.getSubscribeOrderDate())));
                throw yomsException;
            }
        } else {
            if (availabilityTimeSpans.size() == 0) {
                SubscribeOccupiedException yomsException = new SubscribeOccupiedException("预约失败，您选择的时间段已经全部被预约");
                yomsException.setOccupiedTimeSpans(convertTimeSpansToNameWithValues((List<TimeSpan>) invalidTimeSpans,
                        new JDateTime(orderDomain.getSubscribeOrderDate())));
                throw yomsException;
            }
        }

        //移除计划预约的时间段中不可用的时间段
        for (int i = 0; i < invalidTimeSpans.size(); i++) {
            String tempTimeSpan = ((List<TimeSpan>) invalidTimeSpans).get(i).toString();
            expectTimeSpansString.remove(tempTimeSpan);
        }

        //转换预约String类型为TimeSpan类型
        List<TimeSpan> ultimateTimeSpans = expectTimeSpansString
                .stream()
                .map(s -> {
                    TimeSpan timeSpan = new TimeSpan();
                    timeSpan.setStartTimeString(s.split("-")[0]);
                    timeSpan.setEndTimeString(s.split("-")[1]);
                    return timeSpan;
                })
                .collect(toList());

        //转换TimeSpans为long类型
        long ultimateLong = this.convertTimeSpansToLong(ultimateTimeSpans);

        //查找某用户某一天某个场馆的预约订单, 如果存在则修改预约long值,如果不存在则创建一条预约记录
        SubscribeOrderDomain existDomain = getOrderByUSD(orderDomain.getSubscribeOrderUserId(), orderDomain.getSubscribeOrderStadiumId(), orderDomain.getSubscribeOrderDate());
        int result;
        if (existDomain == null) {
            orderDomain.setSubscribeOrderTimeSpan(ultimateLong);
            result = subscribeOrderDao.addDomain(orderDomain);
            if (result != 1) {
                throw new SubscribeErrorException("预约失败，请稍后重试");
            } else {
                return orderDomain;
            }
        } else {
            long tempLong = existDomain.getSubscribeOrderTimeSpan();
            existDomain.setSubscribeOrderTimeSpan(tempLong + ultimateLong);
            result = subscribeOrderDao.updateDomain(existDomain);
            if (result != 1) {
                throw new SubscribeErrorException("预约失败，请稍后重试");
            } else {
                return existDomain;
            }
        }
    }

    /**
     * 转换TimeSpan集合为NameWithValue集合
     *
     * @param timeSpans
     * @param date
     * @return
     */
    private List<NameAndValueModel> convertTimeSpansToNameWithValues(List<TimeSpan> timeSpans, JDateTime date) {
        List<NameAndValueModel> nameAndValueModels = new ArrayList<>();
        for (TimeSpan timeSpan : timeSpans) {
            NameAndValueModel nameAndValueModel = new NameAndValueModel();
            nameAndValueModel.setName(date.toString("YYYY-MM-DD"));
            nameAndValueModel.setValue(timeSpan.getStartTimeString() + "-" + timeSpan.getEndTimeString());

            nameAndValueModels.add(nameAndValueModel);
        }
        return nameAndValueModels;
    }

    /**
     * 根据用户ID，场馆ID，预约日期查找订单。返回的结果只有一条
     *
     * @param uid
     * @param sid
     * @param date
     * @return
     */
    private SubscribeOrderDomain getOrderByUSD(int uid, int sid, Date date) throws Exception {
        SubscribeOrderDomain orderDomain = new SubscribeOrderDomain();
        orderDomain.setSubscribeOrderUserId(uid);
        orderDomain.setSubscribeOrderStadiumId(sid);
        orderDomain.setSubscribeOrderDate(date);

        List<SubscribeOrderDomain> domains = subscribeOrderDao.getDomainByConditionOfSingleTable(orderDomain);
        if (domains.size() == 0) {
            return null;
        } else if (domains.size() == 1) {
            return domains.get(0);
        } else {
            throw new Exception(String.format("用户：%s--预约场馆:%s--出现订单错误", orderDomain.getSubscribeOrderUserId(), orderDomain.getSubscribeOrderStadiumId()));
        }
    }

    /**
     * 将long时间段集合格式转换为Timespans
     *
     * @param sid
     * @param timeSpans
     * @return
     * @throws YomsException
     */
    public List<TimeSpan> convertLongToTimespans(int sid, long timeSpans) throws YomsException {
        TimeSpan businessTimeSpan = stadiumService.getStadiumBusinessTimeSpan(sid);
        List<TimeSpan> timeSpanList = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();

        int minutes = new JDateTime(businessTimeSpan.getStartTime()).getMinute();
        int i = 1;
        while (i <= 24) {
            int k = 1;
            int j = 1;
            while (j++ <= i) {
                k = k * 2;
            }

            if ((timeSpans & k) == k) {
                if (!integers.contains(i)) {
                    int sHour = i - 1;
                    int eHour = i;
                    String startTimeString = String.format("%s:%s", sHour <= 9 ? ("0" + sHour) : sHour, minutes);
                    String endTimeString = String.format("%s:%s", eHour <= 9 ? ("0" + eHour) : eHour, minutes);

                    TimeSpan timeSpan = new TimeSpan();
                    timeSpan.setStartTimeString(startTimeString);
                    timeSpan.setEndTimeString(endTimeString);
                    timeSpanList.add(timeSpan);

                    integers.add(i);
                }
            }
            i++;
        }

        return timeSpanList;
    }

    public List<TimeSpan> convertLongToTimespans(TimeSpan businessTimeSpan, long timeSpans) throws YomsException {
        List<TimeSpan> timeSpanList = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();

        int minutes = new JDateTime(businessTimeSpan.getStartTime()).getMinute();
        int i = 1;
        while (i <= 24) {
            int k = 1;
            int j = 1;
            while (j++ <= i) {
                k = k * 2;
            }

            if ((timeSpans & k) == k) {
                if (!integers.contains(i)) {
                    int sHour = i - 1;
                    int eHour = i;
                    String startTimeString = String.format("%s:%s", sHour <= 9 ? ("0" + sHour) : sHour, minutes);
                    String endTimeString = String.format("%s:%s", eHour <= 9 ? ("0" + eHour) : eHour, minutes);

                    TimeSpan timeSpan = new TimeSpan();
                    timeSpan.setStartTimeString(startTimeString);
                    timeSpan.setEndTimeString(endTimeString);
                    timeSpanList.add(timeSpan);

                    integers.add(i);
                }
            }
            i++;
        }

        return timeSpanList;
    }

    /**
     * 将时间段集合转换为long格式
     *
     * @param timeSpans 要转换的时间段集合
     * @return 代表时间段集合的lang格式，第一位代表 00:xx-01:xx，第二位代表 01:xx-02:xx 以此类推。
     * 其中xx是根据营业时间的分钟产生的，如果营业起始时间为08:00，那么xx就为00
     * @throws YomsException
     */
    public long convertTimeSpansToLong(List<TimeSpan> timeSpans) throws YomsException {
        List<Integer> ints = new ArrayList<>();
        for (TimeSpan timeSpan : timeSpans) {
            int i = new JDateTime(timeSpan.getStartTime()).getHour();
            if (i >= 24 || i < 0) {
                throw new YomsException("时间段非法");
            }
            ints.add(i);
        }

        long longTimeSpans = 0;
        for (Integer i : ints) {
            longTimeSpans += Math.pow(2, i + 1);
        }

        return longTimeSpans;
    }

    /**
     * 检测时间段是否已经被预约
     *
     * @param timeSpan
     * @param stadiumSubscribeDomains
     * @return
     */
    private boolean checkSubscribeTimeSpanIsExist(TimeSpan timeSpan, Collection<SubscribeOrderDomain> stadiumSubscribeDomains) {
        int hour = new JDateTime(timeSpan.getStartTime()).getHour();

        int k = 2;
        int j = 1;
        while (j++ <= hour) {
            k = k * 2;
        }

        for (SubscribeOrderDomain domain : stadiumSubscribeDomains) {
            if ((domain.getSubscribeOrderTimeSpan() & k) == k) {
                return false;
            }
        }

        return true;
    }

    /**
     * 获取某个场馆某一天的所有预约订单
     *
     * @param sid
     * @param date
     * @return
     */
    private Collection<SubscribeOrderDomain> getOneDayOrder(int sid, JDateTime date) {
        SubscribeOrderDomain subscribeOrderDomain = new SubscribeOrderDomain();
        subscribeOrderDomain.setSubscribeOrderStadiumId(sid);
        subscribeOrderDomain.setSubscribeOrderDate(date.convertToDate());

        Collection<SubscribeOrderDomain> stadiumSubscribeDomains = subscribeOrderDao.getDomainByConditionOfSingleTable(subscribeOrderDomain);

        return stadiumSubscribeDomains;
    }

    /**
     * 根据时间类型和场馆分组ID获取统计
     *
     * @param dateType
     * @param gid
     * @return
     */
    public Collection<SubscribeReportFormsDomain> getStadiumGroupStatisticByDateTypeWithGid(int dateType, int gid) {
        DateTimeSpan dateTimeSpan = initGetStadiumGroupStatisticDate(dateType);
        dateTimeSpan.setEndDateTime(new JDateTime().addDay(100).convertToDate());

        Map<String, Integer> tempNameAndValueModels = new HashMap<>();
        Collection<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getSubscribeDateByUidWithSid(null, dateTimeSpan);

        subscribeOrderDomains.stream()
                .map(domain -> getStadiumDoaminByOrder(domain))
                .map(domain -> {
                    String stadiumName = domain.getStadiumDomain().getStadiumName();
                    int stadumId = domain.getStadiumDomain().getStadiumId();
                    String mapKey = stadumId + "&" + stadiumName;
                    int stadiumGroupId = domain.getStadiumDomain().getStadiumGroupId();
                    if (stadiumGroupId == gid) {
                        int subscribeTotal = 0;
                        String tempBinary = Integer.toBinaryString((int) domain.getSubscribeOrderTimeSpan());
                        for (int i = 0; i < tempBinary.length(); i++) {
                            char item = tempBinary.charAt(i);
                            if (item == '1') {
                                subscribeTotal++;
                            }
                        }

                        int value = tempNameAndValueModels.get(mapKey) == null ? 0 : tempNameAndValueModels.get(mapKey);
                        tempNameAndValueModels.put(mapKey, value + subscribeTotal);
                    }

                    return domain;
                })
                .collect(toList());

        Collection<SubscribeReportFormsDomain> subscribeReportFormsDomains = tempNameAndValueModels
                .keySet()
                .stream()
                .map(key -> MapTimeSpanConvertToSubscribeReportFormsDomain(key, tempNameAndValueModels))
                .collect(toList());

        return subscribeReportFormsDomains;
    }

    private SubscribeReportFormsDomain MapTimeSpanConvertToSubscribeReportFormsDomain(String key, Map<String, Integer> tempNameAndValueModels) {
        SubscribeReportFormsDomain subscribeReportFormsDomain = new SubscribeReportFormsDomain();
        subscribeReportFormsDomain.setId(Integer.parseInt(key.split("&")[0]));
        subscribeReportFormsDomain.setDisplayName(key.split("&")[1]);
        subscribeReportFormsDomain.setValue(tempNameAndValueModels.get(key));
        return subscribeReportFormsDomain;
    }

    /**
     * 根据订单查找场馆,并且将查找到场馆赋值给订单对象中的场馆对象
     *
     * @param domain
     * @return
     */
    private SubscribeOrderDomain getStadiumDoaminByOrder(SubscribeOrderDomain domain) {
        try {
            StadiumDomain stadiumDomain = stadiumService.getStadiumById(domain.getSubscribeOrderStadiumId());
            domain.setStadiumDomain(stadiumDomain);
            return domain;
        } catch (YomsException e) {
            return null;
        }
    }

    /**
     * 根据时间查询场馆报表时间类型初始化查询条件的时间变
     *
     * @param dateType
     * @return
     */
    private DateTimeSpan initGetStadiumGroupStatisticDate(int dateType) {
        DateTimeSpan dateTimeSpan = new DateTimeSpan();
        switch (dateType) {
            case 1:
                dateTimeSpan.setStartDateTime(new JDateTime().addDay(-7).convertToDate());
                break;
            case 2:
                dateTimeSpan.setStartDateTime(new JDateTime().addDay(-30).convertToDate());
                break;
            default:
                throw new IllegalArgumentException();
        }
        return dateTimeSpan;
    }

    /**
     * 根据时间类型获取场馆分组统计
     *
     * @param dateType
     * @return
     */
    public Collection<SubscribeReportFormsDomain> getStadiumGroupStatisticByDateType(int dateType) {

        DateTimeSpan dateTimeSpan = this.initGetStadiumGroupStatisticDate(dateType);
        dateTimeSpan.setEndDateTime(new JDateTime().addDay(100).convertToDate());

        Map<String, Integer> tempNameAndValueModels = new HashMap<>();
        Collection<SubscribeOrderDomain> subscribeOrderDomains = subscribeOrderDao.getSubscribeDateByUidWithSid(null, dateTimeSpan);

        subscribeOrderDomains.stream()
                .map(domain -> getStadiumDoaminByOrder(domain))
                .map(domain -> {
                    String groupName = domain.getStadiumDomain().getStadiumGroupDomain().getStadiumGroupName();
                    int groupId = domain.getStadiumDomain().getStadiumGroupDomain().getStadiumGroupId();
                    String mapKey = groupId + "&" + groupName;

                    int subscribeTotal = 0;
                    String tempBinary = Integer.toBinaryString((int) domain.getSubscribeOrderTimeSpan());
                    for (int i = 0; i < tempBinary.length(); i++) {
                        char item = tempBinary.charAt(i);
                        if (item == '1') {
                            subscribeTotal++;
                        }
                    }

                    int value = tempNameAndValueModels.get(mapKey) == null ? 0 : tempNameAndValueModels.get(mapKey);
                    tempNameAndValueModels.put(mapKey, value + subscribeTotal);
                    return domain;
                })
                .collect(toList());

        Collection<SubscribeReportFormsDomain> subscribeReportFormsDomains = tempNameAndValueModels
                .keySet()
                .stream()
                .map(key -> MapTimeSpanConvertToSubscribeReportFormsDomain(key, tempNameAndValueModels))
                .collect(toList());

        return subscribeReportFormsDomains;
    }

    /**
     * 获取某一天可预约的时间段
     *
     * @param sid
     * @param date
     * @return
     * @throws YomsException
     */
    public List<TimeSpan> getStadiumBusinessHours(int sid, JDateTime date) throws YomsException {
        Collection<SubscribeOrderDomain> orderDomains = getOneDayOrder(sid, date);  //获取某一天的所有订单

        TimeSpan businessTimeSpan = stadiumService.getStadiumBusinessTimeSpan(sid);     //获取营业时间段
        List<TimeSpan> timeSpans = businessHours(businessTimeSpan);    //将营业时间段分割为每小时的集合

        List<TimeSpan> resultList = new ArrayList<>();
        for (TimeSpan timeSpan : timeSpans) {
            if (checkSubscribeTimeSpanIsExist(timeSpan, orderDomains)) { //检测营业时间中的时间段是否已经被预约
                if (date.getYear() == yomsHelper.getCurDate().getYear() &&
                        date.getMonth() == yomsHelper.getCurDate().getMonth() &&
                        date.getDay() == yomsHelper.getCurDate().getDay()) {

                    JDateTime curTime = new JDateTime();
                    curTime.set(1970, 1, 1, yomsHelper.getCurDate().getHour(), yomsHelper.getCurDate().getMinute(), yomsHelper.getCurDate().getSecond(), 0);
                    JDateTime startTime = new JDateTime(timeSpan.getStartTime());

                    if (curTime.isBefore(startTime)) {  //筛选当前时间之后的有效营业时间段
                        resultList.add(timeSpan);
                    }
                } else {
                    resultList.add(timeSpan);
                }
            }
        }

        //获取例外计划时间段
        SubscribeExceptionDomain subscribeExceptionDomain = new SubscribeExceptionDomain();
        subscribeExceptionDomain.setStadiumId(sid);
        subscribeExceptionDomain.setExceptionDate(date.convertToDate());

        SubscribeExceptionDomain factSubscribeExceptionDomain = subscribeExceptionService.getSubExceptionDamain(subscribeExceptionDomain);
        Long factSubscribeTimeSpanLong = factSubscribeExceptionDomain.getExceptionTimeBucket();
        Collection<TimeSpan> factSubscribeTimeSpans = this.convertLongToTimespans(sid, factSubscribeTimeSpanLong);
        Collection<String> factSubscribeTimeSpanStrings = factSubscribeTimeSpans
                .stream()
                .map(s -> s.toString())
                .collect(toList());

        //过滤例外计划时间段
        resultList = resultList.stream()
                .map(s -> s.toString())
                .filter(s -> !factSubscribeTimeSpanStrings.contains(s.toString()))
                .map(s -> {
                    TimeSpan timeSpan = new TimeSpan();
                    timeSpan.setStartTimeString(s.split("-")[0]);
                    timeSpan.setEndTimeString(s.split("-")[1]);
                    return timeSpan;
                })

                .collect(toList());

        return resultList;
    }

    /**
     * 将营业时间段划分成单位为一小时一段的集合
     *
     * @throws YomsException
     */
    public List<TimeSpan> businessHours(TimeSpan timeSpan) {
        List<TimeSpan> businessHours = new ArrayList<>();

        JDateTime iterateTime = new JDateTime(timeSpan.getStartTime());
        JDateTime endTime = new JDateTime(timeSpan.getEndTime());

        while (iterateTime.isBefore(endTime)) {
            TimeSpan tsm = new TimeSpan();
            tsm.setStartTime(iterateTime.convertToSqlTime());
            iterateTime = iterateTime.addHour(1);
            tsm.setEndTime(iterateTime.convertToSqlTime());

            businessHours.add(tsm);
        }

        return businessHours;
    }

    /**
     * 转换字符串为TimeSpan
     *
     * @param lists
     * @return
     */
    public List<TimeSpan> convertStringToTimespan(List<String> lists) {
        List<TimeSpan> timeSpans = new ArrayList<>();
        for (String str : lists) {
            String startTimeString = str.split("-")[0];
            String endTimeString = str.split("-")[1];
            TimeSpan timeSpan = new TimeSpan();
            timeSpan.setStartTimeString(startTimeString);
            timeSpan.setEndTimeString(endTimeString);
            timeSpans.add(timeSpan);
        }
        return timeSpans;
    }
}
